package com.skyyan.netty.jjf.util;
import java.math.BigInteger;

/**
 * 卡号（5字节大端序）与10进制字符串互转工具类
 *
 * 核心规则：
 * - **字节序**：大端序 (Big-Endian)，即最高有效字节（高位）存储在数组的最低索引处（最前面）。
 * - **填充规则**：当数值的字节表示长度不足5字节时，在数组的**前面（高位）补0**。
 *
 * 例如：
 *   卡号 1 (十进制) -> 字节数组: [0x00, 0x00, 0x00, 0x00, 0x01]
 *   卡号 256 (十进制) -> 字节数组: [0x00, 0x00, 0x00, 0x01, 0x00]
 *   16进制数组 转换成10进制的时候 先对高位补0的进行去除，然后转为10进制字符串
 */
public class CardNumberUtil {

    /** 卡号固定长度 (字节) */
    public static final int CARD_NUMBER_LENGTH = 5;

    /**
     * 将表示卡号的5字节数组（大端序，高位在前）转换为10进制字符串。
     *
     * @param numberNo 长度为5的字节数组，存储卡号的16进制值，高位在前。
     * @return 卡号的10进制字符串表示。
     * @throws IllegalArgumentException 如果输入数组为null或长度不为5。
     */
    public static String bytesToDecimalString(byte[] numberNo) throws Exception{
        if (numberNo == null) {
            return "";
        }
        if (numberNo.length != CARD_NUMBER_LENGTH) {
            throw new Exception("卡号字节数组长度必须为" + CARD_NUMBER_LENGTH + "，实际长度: " + numberNo.length);
        }

        // 先去除高位的零
        byte[] trimmedBytes = trimLeadingZeros(numberNo);
        
        // 如果全部是零，则返回"0"
        if (trimmedBytes.length == 0) {
            return "0";
        }

        // 创建适当长度的数组，首字节为0x00，确保BigInteger解析为正数（处理符号位）
        byte[] positiveBytes = new byte[trimmedBytes.length + 1]; // 长度+1
        positiveBytes[0] = 0x00; // 显式设置符号位为0
        System.arraycopy(trimmedBytes, 0, positiveBytes, 1, trimmedBytes.length); // 复制字节数据

        BigInteger bigInt = new BigInteger(positiveBytes);
        return bigInt.toString();
    }

    /**
     * 将10进制字符串表示的卡号转换为5字节大端序数组（高位在前，低位在后）。
     * 如果数值的字节表示不足5字节，则在数组的**前面（高位）补0**。
     *
     * @param decimalString 表示卡号的10进制字符串。
     * @return 长度为5的字节数组，大端序，高位补0。
     * @throws IllegalArgumentException 如果输入字符串为null、空、非数字、负数或数值超出5字节范围。
     */
    public static byte[] decimalStringToBytes(String decimalString) {
        if (decimalString == null || decimalString.trim().isEmpty()) {
            throw new IllegalArgumentException("输入的10进制字符串不能为null或空");
        }

        decimalString = decimalString.trim(); // 去除首尾空格

        BigInteger bigInt;
        try {
            bigInt = new BigInteger(decimalString);
        } catch (NumberFormatException e) {
            throw new IllegalArgumentException("输入的字符串不是有效的数字: '" + decimalString + "'", e);
        }

        // 检查是否为负数
        if (bigInt.signum() < 0) {
            throw new IllegalArgumentException("卡号不能为负数: " + decimalString);
        }

        // 检查是否超出5字节无符号整数的范围 (0 到 2^40 - 1)
        BigInteger maxValue = BigInteger.ONE.shiftLeft(8 * CARD_NUMBER_LENGTH).subtract(BigInteger.ONE);
        if (bigInt.compareTo(maxValue) > 0) {
            throw new IllegalArgumentException(
                    String.format("数值超出5字节范围。最大值: %s (%sH), 输入值: %s",
                            maxValue.toString(), maxValue.toString(16).toUpperCase(), decimalString));
        }

        // 将BigInteger转换为字节数组 (大端序)
        byte[] rawBytes = bigInt.toByteArray(); // 得到大端序字节数组

        // --- 关键：处理 "高位补0" 的逻辑 ---
        // 目标：创建一个长度为5的数组，将rawBytes的内容放在低位，并在高位补0。
        byte[] result = new byte[CARD_NUMBER_LENGTH]; // 初始化为全0，正好满足"高位补0"的要求

        // 计算需要从rawBytes复制的起始位置
        // 如果rawBytes长度 <= 5，则从result的 (5 - rawBytes长度) 位置开始复制
        // 如果rawBytes长度 > 5，则说明有前导0x00，需要跳过
        int srcPos = 0; // rawBytes的起始复制位置
        int destPos = CARD_NUMBER_LENGTH - rawBytes.length; // result的起始复制位置

        if (rawBytes.length > CARD_NUMBER_LENGTH) {
            // 这种情况通常是 [0x00, ...] 的6字节或更长数组
            // 我们需要跳过前导的0x00(s)，但最多只能保留5个有效字节
            int excessBytes = rawBytes.length - CARD_NUMBER_LENGTH;
            srcPos = excessBytes; // 跳过前excessBytes个字节
            destPos = 0; // 从result的开头开始放
            // 再次检查跳过后的字节是否仍超出范围（理论上不会，因为前面有范围检查）
            if (rawBytes.length - srcPos > CARD_NUMBER_LENGTH) {
                throw new IllegalArgumentException("转换后的有效字节超出5字节限制");
            }
        }
        // 如果 rawBytes.length <= 5, destPos 自动计算为 (5 - len)，前面的result元素已是0

        // 执行复制
        System.arraycopy(rawBytes, srcPos, result, destPos, rawBytes.length - srcPos);

        return result;
    }

    /**
     * 去除字节数组中的前导零
     * @param bytes 输入字节数组
     * @return 去除前导零后的字节数组
     */
    private static byte[] trimLeadingZeros(byte[] bytes) {
        if (bytes == null || bytes.length == 0) {
            return new byte[0];
        }
        
        int firstNonZero = 0;
        // 找到第一个非零字节的位置
        while (firstNonZero < bytes.length && bytes[firstNonZero] == 0) {
            firstNonZero++;
        }
        
        // 如果所有字节都是零
        if (firstNonZero == bytes.length) {
            return new byte[0];
        }
        
        // 创建新数组并复制非零部分
        int newLength = bytes.length - firstNonZero;
        byte[] result = new byte[newLength];
        System.arraycopy(bytes, firstNonZero, result, 0, newLength);
        return result;
    }

    // --- 互换性测试 ---
    public static void main(String[] args)throws Exception {
        // 测试用例1: 需要高位补多个0
        String decimal1 = "1"; // 二进制: 1, 字节: [0x01]
        System.out.println("=== 测试用例 1 (补4个0) ===");
        System.out.println("输入10进制: " + decimal1);

        byte[] bytes1 = decimalStringToBytes(decimal1);
        System.out.println("转换为 (Hex): " + bytesToHex(bytes1)); // 期望: 00 00 00 00 01

        String back1 = bytesToDecimalString(bytes1);
        System.out.println("转回10进制: " + back1);
        System.out.println("转换正确: " + decimal1.equals(back1));
        System.out.println();

        // 测试用例2: 需要补1个0
        String decimal2 = "4294967296"; // 0x100000000 (9个十六进制位，需要5字节)
        System.out.println("=== 测试用例 2 (补1个0) ===");
        System.out.println("输入10进制: " + decimal2);

        byte[] bytes2 = decimalStringToBytes(decimal2);
        System.out.println("转换为 (Hex): " + bytesToHex(bytes2)); // 期望: 01 00 00 00 00

        String back2 = bytesToDecimalString(bytes2);
        System.out.println("转回10进制: " + back2);
        System.out.println("转换正确: " + decimal2.equals(back2));
        System.out.println();

        // 测试用例3: 正好5字节 (最大值)
        String decimal3 = "1099511627775"; // 0xFFFFFFFFF (10个十六进制位，但值在5字节内)
        System.out.println("=== 测试用例 3 (最大值) ===");
        System.out.println("输入10进制: " + decimal3);

        byte[] bytes3 = decimalStringToBytes(decimal3);
        System.out.println("转换为 (Hex): " + bytesToHex(bytes3)); // 期望: FF FF FF FF FF

        String back3 = bytesToDecimalString(bytes3);
        System.out.println("转回10进制: " + back3);
        System.out.println("转换正确: " + decimal3.equals(back3));
        System.out.println();

        // 测试用例4: 边界值 (2^40 - 1)
        BigInteger maxValue = BigInteger.ONE.shiftLeft(40).subtract(BigInteger.ONE);
        String decimal4 = maxValue.toString();
        System.out.println("=== 测试用例 4 (理论最大值) ===");
        System.out.println("输入10进制: " + decimal4);

        byte[] bytes4 = decimalStringToBytes(decimal4);
        System.out.println("转换为 (Hex): " + bytesToHex(bytes4));

        String back4 = bytesToDecimalString(bytes4);
        System.out.println("转回10进制: " + back4);
        System.out.println("转换正确: " + decimal4.equals(back4));

        byte[] ts = new byte[]{0x00, 0x5D, (byte)0xfd, 0x3B, 0x14};
        System.out.println("zhuanhuan=="+bytesToDecimalString(ts));
    }

    // 辅助方法：将字节数组格式化为十六进制字符串
    private static String bytesToHex(byte[] bytes) {
        if (bytes == null) return "null";
        StringBuilder sb = new StringBuilder();
        for (byte b : bytes) {
            sb.append(String.format("%02X ", b & 0xFF));
        }
        return sb.toString().trim();
    }
}
